﻿#pragma once

// C 语言头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdint.h>
//#include <math.h>
//#include <time.h>
//#include <errno.h>

#ifndef Y2Y_H_
    #define Y2Y_H_


#if defined(_WIN32) || defined(_WIN64)
    #include<io.h>
    #define fseeko _fseeki64
    #define ftello _ftelli64
#endif


#ifdef __cplusplus
extern "C" {
#endif

// 定义一些错误码
//#pragma region 错误码
#pragma pack(push, 1)
enum {
    ERR_LENGTH_OVER_KEY = 1,

    ERR_NO = 0,
    ERR_MEM_ALLOC = -1,

    ERR_FILE_OPEN = -2,
    ERR_FILE_READ = -3,
    ERR_FILE_WRITE = -4,
    ERR_FILE_CLOSE = -5,
    ERR_FILE_EMPTY = -6,
    ERR_FILE_SEEK = -7,
    ERR_FILE_TELL = -8,
    ERR_FILE_NOT_FOUND = -9,
    ERR_FILE_MODE = -10,
    ERR_FILE_POS = -11,

    ERR_PARAMETER = -12,
}; // 应该会创建多实例，但是多线程时，可能还是需要加锁错误码
#pragma pack(pop)


/// <summary>
/// 将错误码转换为字符串，并输出到 buffer
/// </summary>
/// <param name="buffer">：输出缓冲区</param>
/// <param name="err_code">：错误码</param>
void y2_error_msg(char* buffer, char err_code);


/// <summary>
/// 异或指定长度的内存，加密/解密到另一块至少长度相同的内存
/// </summary>
/// <param name="dest">：保存加密/解密内容的目标内存地址</param>
/// <param name="src">：需要加密/解密内容的源内存地址</param>
/// <param name="src_len">：需要加密/解密内容的源内存长度</param>
/// <param name="key">：加密/解密密钥</param>
/// <param name="len">：加密/解密密钥长度</param>
/// <returns>成功则加密/解密后内容的长度</returns>
int64_t xor_mem(char* dest, const char* src, int64_t src_len, const char* key, int16_t len);



// 加解密上下文
typedef struct y2yCtx {
    //FILE* save_file_;
    //FILE* input_file_;
    //const char* save_filename_;
    //const char* enc_dec_content_;
    //const char* temp_content_;
    const char* key_content_;
    //char first_free_flag_;
    //char malloc_flag_;
    uint16_t key_len_;
    uint64_t count_xor_;
} y2yCtx;

/// <summary>
/// 初始化加密/解密上下文，初始化为 NULL 值
/// </summary>
/// <param name="ctx">：加密/解密上下文</param>
void init_y2yCtx(y2yCtx* const ctx);

/// <summary>
/// 释放加密/解密上下文
/// </summary>
/// <param name="ctx">：加密/解密上下文</param>
void free_y2yCtx(y2yCtx* const ctx);

/// <summary>
/// 重置 y2yCtx 计数器，设置计数为 0
/// </summary>
/// <param name="ctx">：加密/解密上下文</param>
void reset_y2yCtx_count(y2yCtx* const ctx);

/// <summary>
/// 使用 y2yCtx 上下文加密/解密指定长度的内存 src 到 dest
/// </summary>
/// <param name="ctx">：加密/解密上下文</param>
/// <param name="dest">：保存加密/解密内容的目标内存地址</param>
/// <param name="src">：需要加密/解密内容的源内存地址</param>
/// <param name="len">：源需要加密/解密内容的内存长度</param>
/// <returns>返回一个错误码，0 表示成功，其他表示失败，可以使用 y2_error_msg 函数获取错误信息</returns>
char y2_xor_mem(y2yCtx* const ctx, char* dest, const char* src, int16_t len);

/// <summary>
/// 从文件加载密钥
/// </summary>
/// <param name="ctx">：加密/解密上下文</param>
/// <param name="key_filename">：密钥文件完整名</param>
/// <param name="len">：密钥长度，大于文件长度或者等于 0 时，使用文件长度</param>
/// <returns>返回一个错误码，0 表示成功，其他表示失败，可以使用 y2_error_msg 函数获取错误信息</returns>
char y2_load_file_key(y2yCtx* const ctx, const char* key_filename, uint16_t len);

/// <summary>
/// 设置密钥内容
/// </summary>
/// <param name="ctx">：加密/解密上下文</param>
/// <param name="key_content">：密钥内容字符串</param>
/// <param name="len">：密钥长度，大于密钥内容长度或者等于 0 时，使用密钥内容长度</param>
/// <returns>返回一个错误码，0 表示成功，其他表示失败，可以使用 y2_error_msg 函数获取错误信息</returns>
char y2_set_key_content(y2yCtx* const ctx, const char* key_content, int16_t len);

/// <summary>
/// 将文件 src_filename 加密/解密到另一个文件 save_filename
/// </summary>
/// <param name="ctx">：加密/解密上下文</param>
/// <param name="src_filename">：源文件完整名</param>
/// <param name="save_filename">：保存文件完整名</param>
/// <param name="mode">：目标文件写入模式，"w" 或 "a" 开头，不能是 "rb" 或 "r"</param>
/// <param name="start_pos">：指定读取源文件起始位置</param>
/// <param name="len">：指定读取源文件长度，等于 0 时或者大于源文件剩余长度时，使用文件剩余长度</param>
/// <returns>返回一个错误码，0 表示成功，其他表示失败，可以使用 y2_error_msg 函数获取错误信息</returns>
char y2_codec_file_to_file(y2yCtx* const ctx, const char* save_filename, const char* mode, const char* src_filename, int64_t start_pos, int64_t len);

/// <summary>
/// 读取文件 src_filename 加密/解密到内存 dest
/// </summary>
/// <param name="ctx">：加密/解密上下文</param>
/// <param name="dest">：保存解密内容的内存地址</param>
/// <param name="dest_len">：保存解密内容的内存长度</param>
/// <param name="src_filename">：源文件完整名</param>
/// <param name="start_pos">：指定读取源文件起始位置</param>
/// <returns>返回一个错误码，0 表示成功，其他表示失败，可以使用 y2_error_msg 函数获取错误信息</returns>
char y2_codec_file_to_mem(y2yCtx* const ctx, char* dest, int64_t dest_len, const char* src_filename, int64_t start_pos);
   
/// <summary>
/// 从内存 src 加密/解密到另一个文件 dest_filename
/// </summary>
/// <param name="ctx">：加密/解密上下文</param>
/// <param name="dest_filename">：保存加密/解密内容的文件完整名</param>
/// <param name="mode">：目标文件写入模式，"w" 或 "a" 开头，不能是 "rb" 或 "r"；总是以二进制模式写入</param>
/// <param name="src">：保存加密/解密内容的内存地址</param>
/// <param name="src_len">：保存加密/解密内容的内存长度</param>
/// <returns>返回一个错误码，0 表示成功，其他表示失败，可以使用 y2_error_msg 函数获取错误信息</returns>
char y2_codec_mem_to_file(y2yCtx* const ctx, const char* dest_filename, const char* mode, const char* src, int64_t src_len);


//#pragma endregion
#ifdef __cplusplus
}
#endif

#endif // Y2Y_H_

// 文件操作特别说明：a+ 表示追加，也就是任何写操作都会在文件末尾进行，移动指针后也是如此，w 所有模式打开的文件会立刻清空；如果需要从指定位置写入覆盖原文件应该使用 r+ 也就是读写模式
// 这里没有实现 r+ 模式，因为懒！而且实际作用不大
